//利用哈希crc10，产生10位哈希结果
//v1.0没有处理冲突
//老化时间改为5120秒
// `include "top_define.v"

module lookup_cam ( 
                      clk,
                      rst_n,

                      // himac_broadcast_frm,
                      // himac_loopback_on_off,
                      mac_dest,
                      port_sour,
                      mac_sour,
                      mac_addr_en,

                      busy,
                      outport,
                      outport_en,
                      broadcast_pkt_pass,
                      broadcast_pkt_ack,
                      unknow_pkt_pass,
                      unkonw_pkt_ack,
                      look_fail, 

                      time_now,

                      //查找需要用到的地址和数据
                      addr_a,
                      data_a,
                      wren_a,
                      q_al,
                      q_al_en,
                      bus2_lookup_q,
                      bus3_lookup_q,
                      bus4_lookup_q,
                      //学习需要用到的地址和数据
                      study_fail,

                      looking,
                      looking1,
                      initing,
                      look_rden,
                      look_update,
/////plazt 9.1
                      hash_dest,
                      hash_sour,
                      hash_en,
                      
                      
                      init_end//zhangjy for test 2013.9.9
                     );
                     
input clk;
input rst_n;

// input       himac_broadcast_frm  ; //是否为himac广播帧
// input       himac_loopback_on_off;
(*mark_debug = "true"*)input[47:0] mac_dest;
(*mark_debug = "true"*)input       mac_addr_en;         //mac地址有效
(*mark_debug = "true"*)input wire[3:0] port_sour;
(*mark_debug = "true"*)input wire[47:0] mac_sour;
(*mark_debug = "true"*)input wire[9:0] hash_sour;
(*mark_debug = "true"*)input wire[12:0] time_now;
(*mark_debug = "true"*)output busy;
(*mark_debug = "true"*)output[3:0] outport;
(*mark_debug = "true"*)output outport_en;
(*mark_debug = "true"*)output wire look_fail;
(*mark_debug = "true"*)input wire  study_fail;

input   broadcast_pkt_pass;
output  broadcast_pkt_ack;
(*mark_debug = "true"*)input   unknow_pkt_pass;
(*mark_debug = "true"*)output  unkonw_pkt_ack;


(*mark_debug = "true"*)output[9:0] addr_a; //mac地址表

//data_a = { 2'd0, sur_mac(48), sour_port(4), time_val, time_now(13) }
(*mark_debug = "true"*) output[71:0] data_a;// mac地址表中的数据
(*mark_debug = "true"*) output       wren_a;
(*mark_debug = "true"*) input[71:0]  q_al;
input        q_al_en;
input[71:0]  bus2_lookup_q;
input[71:0]  bus3_lookup_q;
input[71:0]  bus4_lookup_q;

(*mark_debug = "true"*)output      looking;
(*mark_debug = "true"*)output      looking1;
(*mark_debug = "true"*)output      look_rden;
(*mark_debug = "true"*)output      look_update;
output          initing;
/////9.1

(*mark_debug = "true"*)input [9:0]hash_dest;
(*mark_debug = "true"*)input      hash_en;


output                init_end;//zhangjy for test 2013.9.9

reg busy;
reg[3:0] outport;
reg outport_en;

reg [9:0]  addr_a;
reg [71:0] data_a;
reg wren_a;

reg [9:0]  addr_a_1;
reg [71:0] data_a_1;
reg        wren_a_1;

reg [9:0]  addr_a_2;
reg [71:0] data_a_2;
reg        wren_a_2;
reg looking;
reg looking1;
reg looking2;
reg look_rden;
reg look_update;

reg[47:0] mac_dest_reg,mac_sour_reg;
reg[3:0] port_sour_reg;
// reg[3:0] step;
// reg[2:0] step2;                                     /////plazt 9.17


reg look_fail_r;

reg[9:0]hash_dest_reg,hash_sour_reg;                               ///11.15

reg[2:0] c_state;
reg[2:0] n_state;

reg[2:0] cstate;
reg[2:0] nstate;


reg     himac_broadcast_frm_reg;

reg     init_end;
reg [9:0] init_addr;

reg     broadcast_pkt_ack;
reg     unkonw_pkt_ack;

reg initing;

//temp
// reg study_fail;

always@(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
      port_sour_reg <= 4'h0;
    else if(mac_addr_en)
      port_sour_reg <= port_sour;
    else 
      port_sour_reg <= port_sour_reg;
  end
always@(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
      mac_sour_reg <= 48'h0;
    else if(mac_addr_en)
      mac_sour_reg <= mac_sour;
    else 
      mac_sour_reg <= mac_sour_reg;
  end
always@(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
      mac_dest_reg <= 48'h0;
    else if(mac_addr_en)
      mac_dest_reg <= mac_dest;
    else 
      mac_dest_reg <= mac_dest_reg;
  end
always@(posedge clk or negedge rst_n)
begin
    if(~rst_n)
      hash_sour_reg <= 10'd0;
    else if(hash_en)
      hash_sour_reg <= hash_sour;
    else 
      hash_sour_reg <= hash_sour_reg;
end
//fist_c_c_state_machine--  dest_mac -> SRAM_memory
localparam    INIT          = 3'b001;
localparam    IDLE          = 3'b010;
localparam    LOOK_UP1      = 3'b100;
localparam    LOOK_UP2      = 3'b000;
//second_cc_state_machine-- SRAM_memory-> lookup_sucess/update
localparam   FSM2_INIT   = 3'b001;
localparam   UPDATE_IDLE = 3'b010;
localparam   LOOK_UP3    = 3'b100;
localparam   LOOK_UP4    = 3'b000;

// always @ (posedge clk or negedge rst_n)
// begin
//     if (~rst_n)
//         himac_broadcast_frm_reg     <=  1'b0;
//     else if (mac_addr_en == 1'b1)
//         himac_broadcast_frm_reg     <=  himac_broadcast_frm;
//     else
//         himac_broadcast_frm_reg     <=  himac_broadcast_frm_reg;
// end

always @ (posedge clk or negedge rst_n)begin
    if (~rst_n)
        init_addr       <=  10'd0;
    else if (c_state == INIT)
        init_addr       <=  init_addr + 10'd1;
    else
        init_addr       <=  10'd0;
end

always @ (posedge clk or negedge rst_n)begin
    if (~rst_n)
        init_end        <=  1'b0;
    else if (init_addr == 10'h3ff)  //1024个地址
        init_end        <=  1'b1;
    else
        init_end        <=  init_end;
end
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
      hash_dest_reg <= 10'd0;
    else if(hash_en)
      hash_dest_reg <= hash_dest;
    else 
      hash_dest_reg <= hash_dest_reg;
end
////////////////////////////////////////////
//FSM1_1
always @ (posedge clk or negedge rst_n)begin
  if(~rst_n)
    c_state <=  INIT;
  else
    c_state <=  n_state;
end
//FSM1_2
always @ ( * )begin
  case(c_state)
    INIT : //将地址表所有表项初始化为0
        if (init_end)
            n_state = IDLE;
        else
            n_state = INIT;
    IDLE :
      if(hash_en)
        n_state = LOOK_UP1;
      else
        n_state = IDLE;

    LOOK_UP1:
      n_state = LOOK_UP2;
    LOOK_UP2:
      n_state = IDLE;
    default:
        n_state = IDLE;
  endcase
end
//FSM1_3
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    begin 
      addr_a_1 <= 10'd0;
      data_a_1 <= 72'h0;
      wren_a_1 <= 1'b0;
    end 
    else 
      case(n_state)
      INIT :
      begin
        addr_a_1 <= init_addr;
        data_a_1 <= 72'h0;
        wren_a_1 <= 1'b1;
      end 
      LOOK_UP1:
      begin 
        addr_a_1 <= hash_dest;
        data_a_1 <= 72'h0;
        wren_a_1 <= 1'b0;
      end 
      default:
      begin 
        addr_a_1<= 10'b0;
        data_a_1<= 72'h0;
        wren_a_1<= 1'b0;
      end 
      endcase
end
///////////////////////////////////////////////////////
//FSM2_1
always @ (posedge clk or negedge rst_n)begin
  if(~rst_n)
    cstate <=  FSM2_INIT;
  else
    cstate <=  nstate;
end   
//FSM2_2
always @ ( * )begin
  case(cstate)
    FSM2_INIT : //将地址表所有表项初始化为0
        if (init_end)
            nstate = UPDATE_IDLE;
        else
            nstate = FSM2_INIT;
    UPDATE_IDLE :
      if(q_al_en)
        nstate = LOOK_UP3;
      else
        nstate = UPDATE_IDLE;
    LOOK_UP3:
      if(!study_fail)
        nstate = LOOK_UP4;
      else
        nstate = UPDATE_IDLE;
    LOOK_UP4:
        nstate = UPDATE_IDLE;
    default:
        nstate = UPDATE_IDLE;
  endcase
end
//FSM2_3
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    begin 
      addr_a_2 <= 10'd0;
      data_a_2 <= 72'h0;
      wren_a_2 <= 1'b0;
    end 
    else 
      case(nstate)
      LOOK_UP4:
      begin
        if(!study_fail)
        begin
          addr_a_2 <= hash_sour_reg;
          data_a_2 <= {6'b0,mac_sour_reg,port_sour_reg,1'b1,time_now};
          wren_a_2 <= 1'b1;    
        end
        else 
        begin
          addr_a_2 <= 10'b0;
          data_a_2 <= 72'b0;
          wren_a_2 <= 1'b0;
        end
      end
      default:
      begin 
        addr_a_2 <= 10'b0;
        data_a_2 <= 72'h0;
        wren_a_2 <= 1'b0;
      end 
      endcase
end
///////////////////////////////////////////////////////
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    busy <= 1'b0;
    else if(n_state == IDLE /*|| nstate == UPDATE_IDLE*/ || nstate == LOOK_UP4)
    busy <= 1'b0;
    else 
    busy <= 1'b1;
end
//looking为1表示lookup模块需要控制单播表
//如下情况：1、
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    looking <= 1'b0;
    else if(n_state==LOOK_UP1 || n_state==LOOK_UP2 || n_state==INIT
            ||nstate==LOOK_UP3 || nstate == LOOK_UP4 || nstate==FSM2_INIT
           )
    looking <= 1'b1;
    else 
    looking <= 1'b0;
end
//用于其他三总线数据FIFO的读控制，
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    look_rden <= 1'b0;
    else if(n_state==LOOK_UP2)
    look_rden <= 1'b1;
    else 
    look_rden <= 1'b0;
end
//用于其他三总线的更新控制，保证在本总线更新时能够拿到其他总线的更新数据
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    look_update <= 1'b0;
    else if(nstate==LOOK_UP3)
    look_update <= 1'b1;
    else 
    look_update <= 1'b0;
end


always@(posedge clk or negedge rst_n)begin
  if(~rst_n)
    look_fail_r <= 1'b0;
  else if(n_state == IDLE)
    look_fail_r <= 1'b0;
  else if(mac_dest_reg == 48'hffff_ffff_ffff && n_state==LOOK_UP1 && (broadcast_pkt_pass == 1'b0) )
    look_fail_r <= 1'b1;
  else if(mac_dest_reg == 48'hffff_ffff_ffff && n_state==LOOK_UP1)
    look_fail_r <= 1'b0;
  else if(nstate == LOOK_UP3 && q_al[65:18]==mac_dest_reg && q_al[13]==1'b1)
    look_fail_r <= 1'b0;
  else if(nstate == LOOK_UP3 && bus2_lookup_q[65:18]==mac_dest_reg && bus2_lookup_q[13]==1'b1)
    look_fail_r <= 1'b0;
  else if(nstate == LOOK_UP3 && bus3_lookup_q[65:18]==mac_dest_reg && bus3_lookup_q[13]==1'b1)
    look_fail_r <= 1'b0;
  else if(nstate == LOOK_UP3 && bus4_lookup_q[65:18]==mac_dest_reg && bus4_lookup_q[13]==1'b1)
    look_fail_r <= 1'b0;
  else if(nstate == LOOK_UP3 && unknow_pkt_pass == 1'b0)
    look_fail_r <= 1'b1;
  else 
    look_fail_r <= look_fail_r;
end
assign look_fail = look_fail_r | study_fail;

always@(posedge clk or negedge rst_n)begin
  if(~rst_n)
    outport_en <= 1'b0;
  // else if(mac_dest_reg == 48'hffff_ffff_ffff && n_state==LOOK_UP1)
  //   outport_en <= 1'b1;
  else if(nstate == LOOK_UP3)
    outport_en <= 1'b1;
  else 
    outport_en <= 1'b0;
end
always@(posedge clk or negedge rst_n)begin
  if(~rst_n)
    outport <= 4'h0;
  else if(mac_dest_reg == 48'hffff_ffff_ffff && n_state==LOOK_UP1)
    outport <= 4'hF;
  else if(nstate == LOOK_UP3 && q_al[65:18]==mac_dest_reg && q_al[13]==1'b1)
    outport <= q_al[17:14];
  else if(nstate == LOOK_UP3 && bus2_lookup_q[65:18]==mac_dest_reg && bus2_lookup_q[13]==1'b1)
    outport <= bus2_lookup_q[17:14];
  else if(nstate == LOOK_UP3 && bus3_lookup_q[65:18]==mac_dest_reg && bus3_lookup_q[13]==1'b1)
    outport <= bus3_lookup_q[17:14];
  else if(nstate == LOOK_UP3 && bus4_lookup_q[65:18]==mac_dest_reg && bus4_lookup_q[13]==1'b1)
    outport <= bus4_lookup_q[17:14];
  else if(nstate == LOOK_UP3)
    outport <= 4'hF;
  else 
    outport <= 4'h0;
end
always @ (posedge clk or negedge rst_n)begin
    if (~rst_n)
        broadcast_pkt_ack           <=   1'b0;
    else if ((outport_en == 1'b1) && (outport == 4'hF) && (mac_dest_reg == 48'hffff_ffff_ffff) && (look_fail == 1'b0))
        broadcast_pkt_ack           <=   1'b1;
    else
        broadcast_pkt_ack           <=   1'b0;
end
     
always @ (posedge clk or negedge rst_n)begin
    if (~rst_n)
        unkonw_pkt_ack              <=   1'b0;
    else if ((outport_en == 1'b1) && (outport == 4'hF) && (mac_dest_reg != 48'hffff_ffff_ffff) && (look_fail == 1'b0) && c_state != INIT)
        unkonw_pkt_ack              <=   1'b1;
    else
        unkonw_pkt_ack              <=   1'b0;
end

////////////////////////////////////////////////////////////
always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    initing <= 1'b0;
    else if(n_state==INIT)
    initing <= 1'b1;
    else 
    initing <= 1'b0;
end

always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    looking1 <= 1'b0;
    else if(n_state==LOOK_UP1 /*|| n_state==INIT*/)
    looking1 <= 1'b1;
    else 
    looking1 <= 1'b0;
end

always@(posedge clk or negedge rst_n)begin
    if(~rst_n)
    looking2 <= 1'b0;
    else if(nstate == LOOK_UP4)
    looking2 <= 1'b1;
    else 
    looking2 <= 1'b0;
end

always @(*) begin
    case({(initing || looking1),looking2})
       2'b10:   begin
                addr_a = addr_a_1;
                data_a = data_a_1;
                wren_a = wren_a_1;
                end
       2'b01:   begin
                addr_a = addr_a_2;
                data_a = data_a_2;
                wren_a = wren_a_2;
                end
       default: begin
                addr_a = 'b0;
                data_a = 'b0;
                wren_a = 'b0;
                end
    endcase
end

endmodule